#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <cstring>

#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

static const uint16_t gport = 8888;
static const int glisten = -1;
static const int gbacklog = 8;

using task_t = std::function<void(int, InetAddr &)>;

enum
{
    SOCK_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERROR,
    ACEPT_ERROR
};

class TcpServer
{
public:
    TcpServer(task_t func, uint16_t port = gport)
        : _port(port),
          _listenfd(glisten),
          _isrunning(false),
          _func(func)
    {}

    void Init()
    {
        // 创建套接字
        _listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenfd < 0)
        {
            LOG(FATAL, "server socket creat error\n");
            exit(SOCK_ERROR);
        }
        LOG(DEBUG, "server creat success, lsistenfd: %d\n", _listenfd);

        // bind
        struct sockaddr_in local;
        socklen_t len = sizeof(local);
        memset(&local, 0, len);

        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = ::bind(_listenfd, (struct sockaddr *)&local, len);
        if (n < 0)
        {
            LOG(FATAL, "server bind error\n");
            exit(BIND_ERROR);
        }
        LOG(DEBUG, "server bind success\n");

        // 监听
        n = ::listen(_listenfd, gbacklog);
        if (n < 0)
        {
            LOG(FATAL, "server listen error\n");
            exit(LISTEN_ERROR);
        }
        LOG(DEBUG, "server listen success\n");
    }
    class ThreadData
    {
    public:
        TcpServer *_self;
        int _sockfd;
        InetAddr _addr;
        ThreadData(int &sockfd, TcpServer *self, InetAddr &addr)
            : _sockfd(sockfd), _self(self), _addr(addr)
        {
        }
    };
    void Loop()
    {
        // 子进程不会变成僵尸进程；子进程资源仍然会被保留；父进程想要释放子进程资源,
        //  需要调用wait/waitpid,否则大量被终止的子进程被记录，可能导致系统性能下降或资源耗尽
        // 非阻塞轮询等待: signal(SIGCHLD,handler);//忽略不是阻塞，忽略是信号递达后的一种处理行为;

        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            // accept 接收
            int sockfd = accept(_listenfd, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                LOG(WARNING, "server accept error\n");
                continue;
            }

            InetAddr addr(client);
            LOG(INFO, "get a new link, client info : %s\n", addr.AddrStr().c_str());
            // v2 多线程
            ThreadData *td = new ThreadData(sockfd, this, addr);
            pthread_t tid;
            pthread_create(&tid, NULL, Excute, td);
        }
        _isrunning = false;
    }

    static void *Excute(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *ptd = static_cast<ThreadData *>(args);
        ptd->_self->_func(ptd->_sockfd, ptd->_addr);
        delete ptd;
        return nullptr;
    }

    ~TcpServer()
    {
        if (_listenfd > 0)
            ::close(_listenfd);
    }

private:
    uint16_t _port;
    int _listenfd;
    bool _isrunning;
    task_t _func;
};